package controllers

import (
	"encoding/json"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"cashew.com/cashew_api/dtos"
	"cashew.com/cashew_api/filters"
	"cashew.com/cashew_api/models"
	"cashew.com/cashew_api/services"
	"cashew.com/cashew_api/tasks"
	"cashew.com/cashew_common/configs"
	"cashew.com/cashew_common/constants"
	"cashew.com/cashew_common/utils"
	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"
)

type VideocallController struct {
	BaseController
}

var Redisserver *utils.RedisService

func init() {
	Redisserver = new(utils.RedisService)
}

// Ws接入连接
func (c *VideocallController) WsConnect() {
	req := dtos.WsConnectParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	identity := constants.IDENTITY_NORMAL_USER // 默认为普通用户

	subRegionId := 0 // 子区域ID 默认为0
	regionConfiguration, err := models.GetSub_region_configurationByCode(strings.ToUpper(req.CountryCode))
	if err == nil && regionConfiguration.RegionId != 0 {
		subRegionId = regionConfiguration.RegionId
	}

	// 1. 判断 user_profile 是否存在
	userProfileInfo, _ := models.GetUser_profileByUserId(userId)
	if userProfileInfo != nil && userProfileInfo.UserId > 0 {
		// 如果存在，获取用户身份
		identity = userProfileInfo.Identity

		if req.Gender != 0 && req.Gender != userProfileInfo.Gender {
			userProfileNew := models.User_profile{
				UserId: req.UserId,
				Gender: req.Gender,
			}
			err = models.UpdateUser_profileByUserIdAndFields(&userProfileNew, []string{"gender"})
			if err != nil {
				c.Error("更新用户信息失败:UpdateUser_profileByUserIdAndFields(%+v, %v): %v", &userProfileNew, []string{"gender"}, err)
				c.Result(constants.ERR_WS_UPDATE_USER_FAIL, nil)
			}
		}

	} else {
		// 如果不存在就初始化一条记录
		err = models.InitUserProfile(userId, req.Gender)
		if err != nil {
			c.Error("初始化用户信息失败:InitUserProfile(%v, %v): %v", userId, req.Gender, err)
			c.Result(constants.ERR_INIT_USER_PROFILE_FAIL, nil)
			return
		}
	}

	// 不同身份处理逻辑
	if identity == constants.IDENTITY_NORMAL_USER {
		// 判断用户表是否存在
		existUser := models.ExistUserByUserId(userId)
		if !existUser {
			err = services.InitializeUserInfo(&req, subRegionId)
			if err != nil {
				c.Error("初始化用户信息失败:initializeUserInfo(%+v, %v): %v", req, subRegionId, err)
				c.Result(constants.ERR_WS_ADD_USER_FAIL, nil)
				return
			}
		} else {
			err = services.UpdateUserInfo(&req, subRegionId)
			if err != nil {
				c.Error("更新用户信息失败:updateUserInfo(%+v, %v): %v", req, subRegionId, err)
				c.Result(constants.ERR_WS_UPDATE_USER_FAIL, nil)
				return
			}
		}
	} else {
		err = services.UpdateAnchorInfo(&req, subRegionId)
		if err != nil {
			c.Error("更新主播信息失败:updateAnchorInfo(%+v, %v): %v", req, subRegionId, err)
			c.Result(constants.ERR_WS_UPDATE_ANTHOR_FAIL, nil)
			return
		}
	}

	// 同步更新用户redis缓存
	flagImage := regionConfiguration.FlagImage
	userCache := services.UserCache{
		Gender:      req.Gender,
		SubRegionId: subRegionId,
		Nick:        req.Nick,
		HeadImg:     req.HeadImg,
		Age:         req.Age,
		FlagImage:   flagImage,
	}
	err = services.StoreUserInfoToRedis(Redisserver, req.UserIdstr, &userCache)
	if err != nil {
		c.Error("同步更新用户缓存失败:StoreUserInfoToRedis(%v): %v", req.UserIdstr, err)
		c.Result(constants.ERR_WS_UPDATE_USER_CACHE_FAIL, nil)
		return
	}

	// 获取用户离线消息
	messages, err := services.GetAllOfflineMessages(req.UserIdstr)
	if err != nil {
		c.Error("获取用户离线消息失败:GetAllOfflineMessages(%v): %v", req.UserIdstr, err)
		c.Result(constants.ERR_WS_GET_OFFLINE_MSG_FAIL, nil)
		return
	}
	for _, message := range messages {
		go func() {
			err = services.ReceiveOfflineMessagesOnUserOnline(req.UserIdstr, message)
			if err != nil {
				logs.Error("接收离线消息失败:ReceiveOfflineMessagesOnUserOnline(%v, %v): %v", req.UserIdstr, message, err)
			}
		}()
	}

	// 如果用户call锁存在，则释放用户锁
	_ = Redisserver.ReleaseCallUserLock(req.UserIdstr)

	// 如果用户快速拨打电话请求池里有该用户，则将其从请求池中删除
	_ = services.RemoveUserFromUserPool(req.UserIdstr)

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdstr, constants.DATA_UPDATED_WS_CONNECT, nil)

	//res := map[string]interface{}{
	//	"offline_messages": messages,
	//}

	c.Result(constants.SUCCESS, nil)
	return
}

// Ws断开连接
func (c *VideocallController) WsDisconnect() {
	req := dtos.WsDisconnectParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	// 判断如果用户状态还是通话中，那么找到正在线通话的订单应调用挂断接口
	if identity == constants.IDENTITY_NORMAL_USER {
		userInfo, err := models.GetUsersByUserId(userId)
		if err != nil {
			c.Error("查询用户信息失败:GetUsersByUserId(%v): %v", userId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}
		if userInfo.Status == constants.ANCHOR_BUSY {
			// 找到正在线通话的订单，并调用挂断接口
			lastUserBusyLog, err := models.GetLastUserBusyLog(userId)
			if err == nil && lastUserBusyLog != nil && lastUserBusyLog.Id > 0 {
				operationType := 0
				callerId := lastUserBusyLog.CallerId
				err = services.StopCallLogic(lastUserBusyLog.Id, userId, lastUserBusyLog.AnchorId, lastUserBusyLog.ViewerId, callerId, operationType)
				if err != nil {
					c.Error("StopCallLogic(%s, %v, %v, %v): %v", lastUserBusyLog.Id, userId, lastUserBusyLog.AnchorId, lastUserBusyLog.ViewerId, err)
					c.Result(constants.ERR_STOP_CALL_LOGIC, nil)
					return
				}
			}
		}

		// 最后更新自己的状态为离线
		user := models.Users{
			UserId:     userId,
			Status:     constants.ANCHOR_OFFLINE,
			UpdateTime: time.Now().Unix(),
		}
		err = models.UpdateUsersByUserIdAndFields(&user, []string{"status", "update_time"})
		if err != nil {
			c.Error("UpdateUsersByUserIdAndFields(%+v): %v", &user, err)
			c.Result(constants.ERR_UPDATE_USER_STATUS, nil)
			return
		}
	} else {
		anchorInfo, err := models.GetAnchor_infoByAnchorId(userId)
		if err != nil {
			c.Error("GetAnchor_infoByAnchorId(%v): %v", userId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}
		if anchorInfo.Status == constants.ANCHOR_BUSY {
			// 找到正在线通话的订单，并调用挂断接口
			lastAnchorBusyLog, err := models.GetLastAnchorBusyLog(userId)
			if err == nil && lastAnchorBusyLog != nil && lastAnchorBusyLog.Id > 0 {
				operationType := 0
				callerId := lastAnchorBusyLog.CallerId
				err = services.StopCallLogic(lastAnchorBusyLog.Id, userId, lastAnchorBusyLog.AnchorId, lastAnchorBusyLog.ViewerId, callerId, operationType)
				if err != nil {
					c.Error("StopCallLogic(%s, %v, %v, %v): %v", lastAnchorBusyLog.Id, userId, lastAnchorBusyLog.AnchorId, lastAnchorBusyLog.ViewerId, err)
					c.Result(constants.ERR_STOP_CALL_LOGIC, nil)
					return
				}
			}
		}

		// 最后更新自己的状态为离线
		anchor := models.Anchor_info{
			AnchorId:   userId,
			Status:     constants.ANCHOR_OFFLINE,
			LiveModel:  constants.LIVE_MODEL_LEAVE,
			ActiveTime: time.Now().Unix(),
		}
		err = models.UpdateAnchor_infoByAnchorIdAndFields(&anchor, []string{"status", "live_model", "active_time"})
		if err != nil {
			c.Error("UpdateAnchor_infoByAnchorIdAndFields(%+v): %v", &anchor, err)
			c.Result(constants.ERR_UPDATE_USER_STATUS, nil)
			return
		}
	}

	// 如果用户锁存在，则释放用户锁
	_ = Redisserver.ReleaseCallUserLock(req.UserIdstr)

	// 如果用户快速拨打电话请求池里有该用户，则将其从请求池中删除
	_ = services.RemoveUserFromUserPool(req.UserIdstr)

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdstr, constants.DATA_UPDATED_WS_DISCONNECT, nil)

	c.Result(constants.SUCCESS, nil)
}

// 主播设置接单状态
// 修改anchor_info表中status字段值为3
func (c *VideocallController) AcceptOrder() {

	req := dtos.AcceptOrderParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	// 查看主播当前审核状态
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	if identity != constants.IDENTITY_ANCHOR {
		c.Result(constants.ERR_IDENTITY_NOT_ANCHOR, nil)
		return
	}

	anchorNew := models.Anchor_info{
		AnchorId:   userId,
		Status:     constants.ANCHOR_ONLINETAKINGORDERS,
		ActiveTime: time.Now().Unix(),
	}
	err = models.UpdateAnchor_infoByAnchorIdAndFields(&anchorNew, []string{"status", "active_time"})
	if err != nil {
		c.Error("UpdateAnchor_infoByAnchorIdAndFields(%+v): %v", anchorNew, err)
		c.Result(constants.ERR_ANCHOR_STATUS_UPDATE, nil)
		return
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_FAST_ORDER_UPDATED, nil)

	c.Result(constants.SUCCESS, nil)
	return
}

// 主播取消接单状态
// 修改anchor_info表中status字段值为2
func (c *VideocallController) StopOrder() {
	req := dtos.StopOrderParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId

	anchorInfo := models.Anchor_info{
		AnchorId:   userId,
		Status:     constants.ANCHOR_ONLINEIDLE,
		ActiveTime: time.Now().Unix(),
	}
	err = models.UpdateAnchor_infoByAnchorIdAndFields(&anchorInfo, []string{"status", "active_time"})
	if err != nil {
		c.Error("UpdateAnchor_infoByAnchorIdAndFields(%+v): %v", anchorInfo, err)
		c.Result(constants.ERR_ANCHOR_STATUS_UPDATE, nil)
		return
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdstr, constants.DATA_UPDATED_FAST_ORDER_UPDATED, nil)

	c.Result(constants.SUCCESS, nil)
	return
}

/*
* 主播直播中接单状态设置
1、修改anchor_info表中live_model字段值
*/
func (c *VideocallController) LivingOrderStatusSetting() {

	req := dtos.LivingOrderStatusSettingParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId

	anchorInfo := models.Anchor_info{
		AnchorId:   userId,
		ActiveTime: time.Now().Unix(),
	}

	if req.LiveModel == constants.LIVE_MODEL_LEAVE {
		anchorInfo.Status = constants.ANCHOR_ONLINEIDLE
		anchorInfo.LiveModel = constants.LIVE_MODEL_LEAVE
	} else if req.LiveModel == constants.LIVE_MODEL_LIVE_PLUS {
		anchorInfo.Status = constants.ANCHOR_ONLINETAKINGORDERS
		anchorInfo.LiveModel = constants.LIVE_MODEL_LIVE_PLUS
	} else if req.LiveModel == constants.LIVE_MODEL_LIVE_ONLY {
		anchorInfo.Status = constants.ANCHOR_ONLINEIDLE
		anchorInfo.LiveModel = constants.LIVE_MODEL_LIVE_ONLY
	}

	err = models.UpdateAnchor_infoByAnchorIdAndFields(&anchorInfo, []string{"live_model", "status", "active_time"})
	if err != nil {
		c.Error("UpdateAnchor_infoByAnchorIdAndFields(%+v): %v", anchorInfo, err)
		c.Result(constants.ERR_UPDATE_LIVING_ORDER_STATUS, nil)
		return
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdstr, constants.DATA_UPDATED_LIVE_MODEL_UPDATED, nil)

	c.Result(constants.SUCCESS, nil)
	return
}

// 获得用户剩余聊天次数
func (c *VideocallController) GetFreeChatCount() {
	req := dtos.GetFreeChatCountParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	//var viewerId, anchorId int64
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)
	//if identity == constants.IDENTITY_NORMAL_USER {
	//	viewerId = req.UserId
	//	anchorId = toUserId
	//} else {
	//	viewerId = toUserId
	//	anchorId = req.UserId
	//}

	//// 普通用户需要返回解锁聊天费用
	//videoCallPriceConfig, err := models.GetVideo_call_price_configByPriceType(constants.PRICE_TYPE_ID_CHAT)
	//if err != nil {
	//	c.Error("GetVideo_call_price_configByPriceType(%d): %v", constants.PRICE_TYPE_ID_CHAT, err)
	//	c.Result(constants.ERR_GET_CALL_PRICE_CONFIG, nil)
	//	return
	//}

	// 判断黑名单
	blackStatus := models.BlackStatus(userId, toUserId)

	res := dtos.GetFreeChatCountRes{
		UserId:      req.UserIdStr,
		Identity:    identity,
		BlackStatus: blackStatus,
		//ChatPayPrice: videoCallPriceConfig.PayPrice,
		ChatPayPrice: 0,
	}

	// 判断解锁聊天
	//isUnlock := models.IsUnlock(viewerId, anchorId)
	//res.ChatUnlock = isUnlock
	res.ChatUnlock = true // 现在默认都是解锁的

	//if identity == constants.IDENTITY_NORMAL_USER {
	//	userInfo, err := models.GetUsersByUserId(userId)
	//	if err != nil {
	//		c.Error("获取用户信息失败:GetUsersByUserId(%v): %v", userId, err)
	//		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	//		return
	//	}
	//	res.FreeCount = userInfo.FreeChatCount
	//}

	c.Result(constants.SUCCESS, res)
}

/*
* 发送私聊消息
* 私聊内容包括文字、语音、图片
1、判断发送聊天消息的是主播还是男性用户

	（1）客户端传用户id,根据用户id查询用户信息，判断用户是否为主播
	（2）如果是主播就能随便聊
	（3）如果是普通用户则判断是否已解锁，聊天解锁表
	（4）如果未解锁则提示男性用户需要先解锁聊天

2、如果是主播则直接调用IM接口将消息发送给男性用户
3、如果是男性用户则判断是否可以聊天

	（1）已解锁则调用IM接口将消息发送给主播
	（2）未解锁则判断是否有免费聊天次数
	（3）有免费聊天次数先扣一次聊天次数再调用IM接口将消息发送给主播
	（4）无免费聊天次数则返回给用户需先付费再聊条
*/
func (c *VideocallController) SendChat() {

	req := dtos.SendChatParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)

	var anchorId, viewerId int64
	payPrice := int64(0)

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	// 申请中的主播限制聊天
	if identity == constants.IDENTITY_APPLY_ANCHOR {
		c.Result(constants.ERR_NO_CHAT_PERMISSION, nil)
		return
	}

	toUserIdentity, err := models.GetUserIdentityByUserId(toUserId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	// 必须是一个普通用户和一个主播才能聊天
	if identity+toUserIdentity != constants.IDENTITY_NORMAL_USER+constants.IDENTITY_ANCHOR {
		c.Error("没有聊天权限 用户：%d 身份identity:%d 不能与用户：%d 身份toUserIdentity: %d 聊天", userId, identity, toUserId, toUserIdentity)
		c.Result(constants.ERR_NO_CHAT_PERMISSION, nil)
		return
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		anchorId = toUserId
		viewerId = userId
	} else {
		anchorId = userId
		viewerId = toUserId
	}

	// 判断黑名单
	blackStatus := models.BlackStatus(userId, toUserId)
	if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
		c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
		return
	} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
		c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
		return
	}

	res := dtos.SendChatRes{
		//ChatUnlock:   chatUnlock,
		ChatPayPrice: payPrice,
		Identity:     identity,
		FreeCount:    0,
		SendStatus:   true,
	}

	// 判断解锁聊天
	isUnlock := models.IsUnlock(viewerId, anchorId)
	res.ChatUnlock = isUnlock

	// 用户私聊需要解锁聊天
	if identity == constants.IDENTITY_NORMAL_USER && req.ChatScenario == constants.CHAT_SCENARIO_PRIVATE {
		// 普通用户需要返回解锁聊天费用
		videoCallPriceConfig, err := models.GetVideo_call_price_configByPriceType(constants.PRICE_TYPE_ID_CHAT)
		if err != nil {
			c.Error("GetVideo_call_price_configByPriceType(%d): %v", constants.PRICE_TYPE_ID_CHAT, err)
			c.Result(constants.ERR_GET_CALL_PRICE_CONFIG, nil)
			return
		}

		payPrice = videoCallPriceConfig.PayPrice
		res.ChatPayPrice = payPrice

		// 判断免费聊天次数
		userInfo, err := models.GetUsersByUserId(userId)
		if err != nil {
			c.Error("获取用户信息失败:GetUsersByUserId(%v): %v", userId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}

		res.FreeCount = userInfo.FreeChatCount
		// 如果免费聊天次数用完，并且未解锁聊天，则提示用户需先解锁聊天
		if userInfo.FreeChatCount <= 0 && !isUnlock {
			res.SendStatus = false
			c.Result(constants.SUCCESS, res)
			return
		} else {
			// 免费聊天次数减1
			userInfo.FreeChatCount = userInfo.FreeChatCount - 1
			err = models.DecrementUsersFreeChatCount(userId, 1)
			if err != nil {
				c.Error("DecrementUsersFreeChatCount(%d): %v", userId, err)
				c.Result(constants.ERR_UPDATE_USER_FREE_CHAT_COUNT, nil)
				return
			}
			res.FreeCount = userInfo.FreeChatCount
		}
	}

	// IM 通知
	// 查询接收消息的用户在线状态，判断发离线消息还是在线消息
	isOnline := true
	if identity == constants.IDENTITY_NORMAL_USER {
		toUserInfo, err := models.GetAnchor_infoByAnchorId(anchorId)
		if err != nil {
			c.Error("GetAnchor_infoByAnchorId(%v): %v", anchorId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	} else {
		toUserInfo, err := models.GetUsersByUserId(viewerId)
		if err != nil {
			c.Error("GetUsersByUserId(%v): %v", viewerId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	}

	// 查询用户信息详情【读缓存】
	strAnchorId := fmt.Sprintf("%d", anchorId)
	anchorCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strAnchorId, constants.IDENTITY_ANCHOR)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}
	strViewerId := fmt.Sprintf("%d", viewerId)
	viewerCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strViewerId, constants.IDENTITY_NORMAL_USER)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}

	noticeUserId := req.ToUserId
	reqIm := services.AssembleImChatRequest(&req, identity, noticeUserId, isOnline, viewerCacheInfo, anchorCacheInfo)
	err = services.ImChat(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imChat(%+v): %v", req, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// 如果用户离线，发应用外推送消息
	logs.Debug("用户%d(%s)发给用户%d(%s)的私聊消息，是否在线：%t", userId, toUserId, isOnline)
	if !isOnline {
		logs.Debug("发送离线消息")
		reqCoco, _ := services.AssembleCocoPushMessageReq(identity, &req, viewerCacheInfo, anchorCacheInfo)
		_ = tasks.SubmitCocoPushTask(reqCoco)
	}
	c.Result(constants.SUCCESS, res)
	return
}

// SendGift 发送礼物
func (c *VideocallController) SendGift() {

	req := dtos.SendGiftParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)

	blackStatus := models.BlackStatus(userId, toUserId)
	if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
		c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
		return
	} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
		c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
		return
	}

	var anchorId, viewerId int64

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	toUserIdentity, err := models.GetUserIdentityByUserId(toUserId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	// 必须是一个普通用户和一个主播才能聊天
	if identity+toUserIdentity != constants.IDENTITY_NORMAL_USER+constants.IDENTITY_ANCHOR {
		c.Error("没有聊天权限 用户：%d 身份identity:%d 不能与用户：%d 身份toUserIdentity: %d 聊天", userId, identity, toUserId, toUserIdentity)
		c.Result(constants.ERR_NO_CHAT_PERMISSION, nil)
		return
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		anchorId = toUserId
		viewerId = userId
	} else {
		anchorId = userId
		viewerId = toUserId
	}

	// 调用coco送礼接口，将coco送礼接口返回值返回给客户端
	respData, err := services.CocoSendGift(req.UserIdstr, req.ToUserId, req.PropId, req.PropNum)
	if err != nil {
		c.Error("请求coco送礼接口报错：services.CocoSendGift(%s, %s, %d, %d): %v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, err)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}
	//0成功，-1币不足，-2道具已下架,-3其他错误
	if respData.Result == -1 {
		c.Warn("请求coco送礼接口返回余额不足services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_USER_LOW_BALANCE, nil)
		return
	} else if respData.Result == -2 {
		c.Warn("请求coco送礼接口返回道具已下架services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_GIFT_TAKEN_OFF_SHELVE, nil)
		return
	} else if respData.Result != 0 {
		c.Error("请求coco送礼接口返回其他错误services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}

	// 获得积分
	score, _ := strconv.Atoi(respData.Data.Score)
	// 对聊中需要补充送礼成功后videocall_log表中的礼物收益累加的逻辑
	// 如果是对聊，并且是用户给主播送礼，才需要记录日志，普通聊天和主播给男用户送礼暂不处理
	if req.Guid != "" && respData.Data.Score != "" && req.ChatScenario == constants.CHAT_SCENARIO_VIDEO_CALL && identity == constants.IDENTITY_NORMAL_USER {
		guid, _ := strconv.ParseInt(req.Guid, 10, 64)

		// 验证video_call_log记录是否存在
		checked := models.VerifyLogByGuidAnchorIdAndViewerId(guid, anchorId, viewerId)
		if !checked {
			c.Error("video_call_log记录不存在：guid:%d, anchorId:%d, viewerId:%d", guid, anchorId, viewerId)
			c.Result(constants.ERR_VIDEO_CALL_LOG_NOT_EXIST, nil)
			return
		}
		// 记录礼物收益
		err = models.IncrementVideo_call_logForField(guid, "gift_income", score)
		if err != nil {
			c.Error("IncrementVideo_call_logForField(%d, %d): %v", guid, score, err)
			c.Result(constants.ERR_VIDEO_CALL_LOG_UPDATE_FAILD, nil)
			return
		}
	}

	// IM 通知
	// 查询用户信息详情【读缓存】
	strAnchorId := fmt.Sprintf("%d", anchorId)
	anchorCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strAnchorId, constants.IDENTITY_ANCHOR)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}
	strViewerId := fmt.Sprintf("%d", viewerId)
	viewerCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strViewerId, constants.IDENTITY_NORMAL_USER)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}

	// 查询接收消息的用户在线状态，判断发离线消息还是在线消息
	isOnline := true
	toUserInfo, err := models.GetAnchor_infoByAnchorId(anchorId)
	if err != nil {
		c.Error("GetAnchor_infoByAnchorId(%v): %v", anchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
		return
	}
	if toUserInfo.Status == constants.ANCHOR_OFFLINE {
		isOnline = false
	}

	// 发送用户币值变更消息
	noticeUserId := req.UserIdstr
	userBalance := respData.Data.Balance
	reqUserAmountChangeMsg := services.AssembleImUserAmountChangeMsgRequest(noticeUserId, req.UserIdstr, userBalance, 0, "")
	_ = tasks.SubmitBalanceChangeNoticeTask(&reqUserAmountChangeMsg)

	// 发在线消息
	noticeUserId = req.ToUserId
	reqIm := services.AssembleImSendGiftRequest(&req, identity, noticeUserId, isOnline, &respData.Odds, viewerCacheInfo, anchorCacheInfo)
	err = services.ImSendGift(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imSendGift(%+v): %v", reqIm, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// IM 通知自己
	reqIm.NoticeUserId = req.UserIdstr
	err = services.ImSendGift(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imSendGift(%+v): %v", reqIm, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// 统计相关(只有给主播送礼才统计)
	if toUserIdentity == constants.IDENTITY_ANCHOR {
		_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_IM_GIFT_SCORE, int64(score))
	}

	c.Result(constants.SUCCESS, nil)
	return
}

/*
* 发送私聊消息 取消限制版本（包括：解锁聊天，身份验证，免费聊天次数）
 */
func (c *VideocallController) SendChat2() {

	req := dtos.SendChatParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)
	payPrice := int64(0)

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	toUserIdentity, err := models.GetUserIdentityByUserId(toUserId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	// 判断黑名单
	blackStatus := models.BlackStatus(userId, toUserId)
	if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
		c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
		return
	} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
		c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
		return
	}

	res := dtos.SendChatRes{
		ChatUnlock:   true,
		ChatPayPrice: payPrice,
		Identity:     identity,
		FreeCount:    0,
		SendStatus:   true,
	}

	// IM 通知
	// 查询接收消息的用户在线状态，判断发离线消息还是在线消息
	isOnline := true
	if toUserIdentity == constants.IDENTITY_ANCHOR || toUserIdentity == constants.IDENTITY_APPLY_ANCHOR {
		toUserInfo, err := models.GetAnchor_infoByAnchorId(toUserId)
		if err != nil {
			c.Error("GetAnchor_infoByAnchorId(%v): %v", toUserId, err)
			c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	} else {
		toUserInfo, err := models.GetUsersByUserId(toUserId)
		if err != nil {
			c.Error("GetUsersByUserId(%v): %v", toUserId, err)
			c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	}

	// 查询用户信息详情【读缓存】
	fromUserCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, req.UserIdstr, identity)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", req.UserIdstr, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	toUserCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, req.ToUserId, toUserIdentity)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", req.ToUserId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	noticeUserId := req.ToUserId
	reqIm := services.AssembleImChatRequest2(&req, identity, noticeUserId, isOnline, fromUserCacheInfo, toUserCacheInfo)
	err = services.ImChat(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imChat(%+v): %v", req, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// 如果用户离线，发应用外推送消息
	logs.Debug("用户%d(%s)发给用户%d(%s)的私聊消息，是否在线：%t", userId, toUserId, isOnline)
	if !isOnline {
		logs.Debug("发送离线消息")
		reqCoco, _ := services.AssembleCocoPushMessageReq3(&req, fromUserCacheInfo)
		_ = tasks.SubmitCocoPushTask(reqCoco)
	}
	c.Result(constants.SUCCESS, res)
	return
}

// SendGift2 发送礼物 取消限制版本（包括：解锁聊天，身份验证，免费聊天次数）
func (c *VideocallController) SendGift2() {

	req := dtos.SendGiftParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)

	blackStatus := models.BlackStatus(userId, toUserId)
	if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
		c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
		return
	} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
		c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
		return
	}

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	toUserIdentity, err := models.GetUserIdentityByUserId(toUserId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	// 调用coco送礼接口，将coco送礼接口返回值返回给客户端
	respData, err := services.CocoSendGift(req.UserIdstr, req.ToUserId, req.PropId, req.PropNum)
	if err != nil {
		c.Error("请求coco送礼接口报错：services.CocoSendGift(%s, %s, %d, %d): %v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, err)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}
	//0成功，-1币不足，-2道具已下架,-3其他错误
	if respData.Result == -1 {
		c.Warn("请求coco送礼接口返回余额不足services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_USER_LOW_BALANCE, nil)
		return
	} else if respData.Result == -2 {
		c.Warn("请求coco送礼接口返回道具已下架services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_GIFT_TAKEN_OFF_SHELVE, nil)
		return
	} else if respData.Result != 0 {
		c.Error("请求coco送礼接口返回其他错误services.CocoSendGift(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.ToUserId, req.PropId, req.PropNum, respData)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}

	// 获得积分
	score, _ := strconv.Atoi(respData.Data.Score)
	// 对聊中需要补充送礼成功后videocall_log表中的礼物收益累加的逻辑
	// 如果是对聊，并且是用户给主播送礼，才需要记录日志，普通聊天和主播给男用户送礼暂不处理
	if req.Guid != "" && respData.Data.Score != "" && req.ChatScenario == constants.CHAT_SCENARIO_VIDEO_CALL && toUserIdentity == constants.IDENTITY_ANCHOR {
		guid, _ := strconv.ParseInt(req.Guid, 10, 64)

		// 验证video_call_log记录是否存在
		checked := models.VerifyLogByGuidAnchorIdAndViewerId(guid, toUserId, userId)
		if !checked {
			c.Error("video_call_log记录不存在：guid:%d, anchorId:%d, viewerId:%d", guid, toUserId, userId)
			c.Result(constants.ERR_VIDEO_CALL_LOG_NOT_EXIST, nil)
			return
		}
		// 记录礼物收益
		err = models.IncrementVideo_call_logForField(guid, "gift_income", score)
		if err != nil {
			c.Error("IncrementVideo_call_logForField(%d, %d): %v", guid, score, err)
			c.Result(constants.ERR_VIDEO_CALL_LOG_UPDATE_FAILD, nil)
			return
		}
	}

	// IM 通知
	// 查询接收消息的用户在线状态，判断发离线消息还是在线消息
	isOnline := true
	if toUserIdentity == constants.IDENTITY_ANCHOR || toUserIdentity == constants.IDENTITY_APPLY_ANCHOR {
		toUserInfo, err := models.GetAnchor_infoByAnchorId(toUserId)
		if err != nil {
			c.Error("GetAnchor_infoByAnchorId(%v): %v", toUserId, err)
			c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	} else {
		toUserInfo, err := models.GetUsersByUserId(toUserId)
		if err != nil {
			c.Error("GetUsersByUserId(%v): %v", toUserId, err)
			c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
			return
		}
		if toUserInfo.Status == constants.ANCHOR_OFFLINE {
			isOnline = false
		}
	}

	// 查询用户信息详情【读缓存】
	fromUserCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, req.UserIdstr, identity)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", req.UserIdstr, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	toUserCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, req.ToUserId, toUserIdentity)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", req.ToUserId, err)
		c.Result(constants.ERR_SEND_MSG_VERSION_LOW, nil)
		return
	}

	// 发送用户币值变更消息
	noticeUserId := req.UserIdstr
	userBalance := respData.Data.Balance
	reqUserAmountChangeMsg := services.AssembleImUserAmountChangeMsgRequest(noticeUserId, req.UserIdstr, userBalance, 0, "")
	_ = tasks.SubmitBalanceChangeNoticeTask(&reqUserAmountChangeMsg)

	// 发在线消息
	noticeUserId = req.ToUserId
	reqIm := services.AssembleImSendGiftRequest2(&req, noticeUserId, isOnline, &respData.Odds, fromUserCacheInfo, toUserCacheInfo)
	err = services.ImSendGift(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imSendGift(%+v): %v", reqIm, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// IM 通知自己
	reqIm.NoticeUserId = req.UserIdstr
	err = services.ImSendGift(&reqIm)
	if err != nil {
		c.Error("IM 通知失败：imSendGift(%+v): %v", reqIm, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// 统计相关(只有给主播送礼才统计)
	if toUserIdentity == constants.IDENTITY_ANCHOR {
		_ = models.AnchorWorkStatInc(toUserId, models.ANCHOR_STAT_TYPE_IM_GIFT_SCORE, int64(score))
	}

	c.Result(constants.SUCCESS, nil)
	return
}

func (c *VideocallController) PaychatUnlock() {
	req := dtos.PaychatUnlockParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	anchorId, _ := strconv.ParseInt(req.AnchorId, 10, 64)

	// 状态检查，避免重复解锁
	isUnlock := models.IsUnlock(userId, anchorId)
	if isUnlock {
		c.Error("解锁聊天失败，已解锁聊天：AnchorId:%d, ViewerId:%d \n", anchorId, userId)
		c.Result(constants.ERR_CHAT_ALREADY_UNLOCK, nil)
		return
	}

	// 验证价格
	// 查询解锁聊天费用
	videoCallPriceConfig, err := models.GetVideo_call_price_configByPriceType(constants.PRICE_TYPE_ID_CHAT)
	if err != nil {
		c.Error("GetVideo_call_price_configByPriceType(%d): %v", constants.PRICE_TYPE_ID_CHAT, err)
		c.Result(constants.ERR_GET_CALL_PRICE_CONFIG, nil)
		return
	}

	payPrice := videoCallPriceConfig.PayPrice
	if payPrice != int64(req.Amount) {
		c.Error("支付金额不正确：payPrice:%d, req.Amount:%d", payPrice, req.Amount)
		c.Result(constants.ERR_PAY_AMOUNT_NOT_MATCH, nil)
		return
	}

	// 调 coco api 扣费接口
	respData, err := services.CocoPay(req.UserIdstr, req.AnchorId, req.Amount, 0)
	if err != nil {
		c.Error("请求coco支付失败services.Pay(%s, %s, %d, %d): %v", req.UserIdstr, req.AnchorId, req.Amount, 0, err)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}
	if respData.Result != 0 {
		c.Warn("请求coco支付返回失败结果services.Pay(%s, %s, %d, %d): return  respBody:%+v", req.UserIdstr, req.AnchorId, req.Amount, 0, respData)
		c.Result(constants.ERR_PAY_FAILD, nil)
		return
	}

	// 解锁聊天
	chatUnlockRelation := models.Chat_unlock_relation{
		AnchorId:   anchorId,
		ViewerId:   userId,
		CreateTime: time.Now().Unix(),
	}
	_, err = models.AddChat_unlock_relation(&chatUnlockRelation)
	if err != nil {
		c.Error("解锁聊天失败：AddChat_unlock_relation(%+v): %v", chatUnlockRelation, err)
		c.Result(constants.ERR_CHAT_UNLOCK_FAILD, nil)
		return
	}

	// 发送用户币值变更消息
	noticeUserId := req.UserIdstr
	userBalance := respData.Data.Balance
	reqUserAmountChangeMsg := services.AssembleImUserAmountChangeMsgRequest(noticeUserId, req.UserIdstr, userBalance, 0, "")
	_ = tasks.SubmitBalanceChangeNoticeTask(&reqUserAmountChangeMsg)

	// 统计相关
	_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_PRIVATE_MESSAGE_UNLOCKED_COUNT, 1)

	c.Result(constants.SUCCESS, nil)
}

/*
* 普通用户打电话(请求对聊)
1、调用IM接口通知女性用户拨打请求
2、返回给男性用户已拨打等待中
*/
func (c *VideocallController) Call() {

	req := dtos.CallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	toUserIdentity, err := models.GetUserIdentityByUserId(toUserId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	// 必须是一个普通用户和一个主播才能聊天
	if identity+toUserIdentity != constants.IDENTITY_NORMAL_USER+constants.IDENTITY_ANCHOR {
		c.Error("没有聊天权限 用户：%d 身份identity:%d 不能与用户：%d 身份toUserIdentity: %d 对聊", userId, identity, toUserId, toUserIdentity)
		c.Result(constants.ERR_NO_CALL_PERMISSION, nil)
		return
	}

	var anchorId, viewerId int64
	var toUserStatus int // 对方用户状态,用来判断用户离线时也要发给自己消息

	if identity == constants.IDENTITY_NORMAL_USER {
		anchorId = toUserId
		viewerId = userId
	} else {
		anchorId = userId
		viewerId = toUserId
	}

	// 判断黑名单
	blackStatus := models.BlackStatus(userId, toUserId)
	if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
		c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
		return
	} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
		c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
		return
	}

	// 如果是男性判断至少有1分钟使用的金币余额
	if identity == constants.IDENTITY_NORMAL_USER {
		//isChecked, _, _, err := services.CheckMaleCallCost(viewerId, anchorId)
		checkRes, err := services.CheckMaleCallCost(viewerId, anchorId)
		if err != nil {
			c.Error("CheckMaleCallCost(%v, %v): %v", viewerId, anchorId, err)
			c.Result(constants.ERR_SYS, nil)
			return
		}
		if !checkRes.Checked {
			c.Result(constants.ERR_USER_LOW_BALANCE, nil)
			return
		}

		// 1、判断anchor_info表中status状态是否为可接听状态
		anchorInfo, err := models.GetAnchor_infoByAnchorId(anchorId)
		if err != nil {
			c.Error("GetAnchor_infoByAnchorId(%v): %v", anchorId, err)
			c.Result(constants.ERR_GET_ANCHOR, nil)
			return
		}
		toUserStatus = anchorInfo.Status
		//if (anchorInfo.Status != constants.ANCHOR_ONLINEIDLE && anchorInfo.Status != constants.ANCHOR_ONLINETAKINGORDERS) || anchorInfo.LiveModel != constants.LIVE_MODEL_LIVE_PLUS || anchorInfo.DisableStatus != constants.DISABLE_NONE {
		if toUserStatus == constants.ANCHOR_BUSY || anchorInfo.LiveModel == constants.LIVE_MODEL_LIVE_ONLY || anchorInfo.DisableStatus == constants.DISABLE_BY_MATERIALS {
			c.Result(constants.ERR_ANCHOR_NOT_ONLINETAKINGORDERS, nil)
			return
		}
	} else {
		// 1、判断user表中status状态是否为可接听状态
		userInfo, err := models.GetUsersByUserId(viewerId)
		if err != nil {
			c.Error("GetUsersByUserId(%v): %v", viewerId, err)
			c.Result(constants.ERR_USER_NOT_EXIST, nil)
			return
		}
		toUserStatus = userInfo.Status
		if toUserStatus == constants.ANCHOR_BUSY {
			c.Result(constants.ERR_CALL_USER_BUSY, nil)
			return
		}
	}

	// 查询主播价格信息
	priceInfo, err := services.GetAnchorPriceInfo(anchorId, constants.PRICE_TYPE_ID_VIDEO_CALL)
	if err != nil {
		c.Error("GetAnchorPriceInfo(%v): %v", anchorId, err)
		c.Result(constants.ERR_GET_CALL_PRICE, nil)
		return
	}
	payPrice := priceInfo.PayPrice
	anchorPrice := priceInfo.AnchorPrice

	// 免费卡信息，用户是否是免费卡通话，免费通话卡时间
	isFreeCard := false
	freeCallNum, freeCallCardTime, err := services.CheckFreeCallCard(viewerId)
	if err != nil {
		c.Error("CheckFreeCallCard(%v): %v", viewerId, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}
	if freeCallNum > 0 {
		isFreeCard = true
	}

	// 查询用户信息详情【读缓存】
	strAnchorId := fmt.Sprintf("%d", anchorId)
	anchorCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strAnchorId, constants.IDENTITY_ANCHOR)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}
	strViewerId := fmt.Sprintf("%d", viewerId)
	viewerCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strViewerId, constants.IDENTITY_NORMAL_USER)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}

	// 发消息前，验证对方是否被锁定(30秒，多3秒留给客户端判断挂断重复消息)
	canCall, err := Redisserver.AcquireCallUserLock(req.ToUserId, 33)
	if err != nil {
		c.Error("AcquireCallUserLock(%v): %v", req.ToUserId, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}
	if !canCall {
		c.Result(constants.ERR_GET_CALL_LOCK_FAIL, nil)
		return
	}

	// 发送IM消息1 通知对方
	noticeUserId := req.ToUserId
	// 获取通话权限后，给自己也加锁，防止中途有别人打给自己
	_ = Redisserver.ReSetCallUserLock(req.UserIdStr, 33)

	// 如果对方不在线，先做不发通知（反正也通知不成功）
	if toUserStatus != constants.ANCHOR_OFFLINE {
		reqIm := services.AssembleImVideocallCallRequest(&req, identity, noticeUserId, payPrice, anchorPrice, anchorId, isFreeCard, freeCallCardTime, viewerCacheInfo, anchorCacheInfo)
		err = services.ImVideocallCall(&reqIm)
		if err != nil {
			_ = Redisserver.ReleaseCallUserLock(req.ToUserId)
			_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
			c.Error("IM发布请求失败：videocallCall(%+v): %v", reqIm, err)
			c.Result(constants.ERR_IM_SENDM_FAIL, nil)
			return
		}
	}

	// 发送IM消息2 通知自己
	noticeUserId = req.UserIdStr
	reqIm2 := services.AssembleImVideocallCallRequest(&req, identity, noticeUserId, payPrice, anchorPrice, anchorId, isFreeCard, freeCallCardTime, viewerCacheInfo, anchorCacheInfo)
	err = services.ImVideocallCall(&reqIm2)
	if err != nil {
		_ = Redisserver.ReleaseCallUserLock(req.ToUserId)
		_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		c.Error("IM发布请求失败：videocallCall(%+v): %v", reqIm2, err)
		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
		return
	}

	// 统计相关
	if identity == constants.IDENTITY_NORMAL_USER && toUserIdentity == constants.IDENTITY_ANCHOR {
		_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_CALLED_REQUEST_COUNT, 1)
	} else if identity == constants.IDENTITY_ANCHOR && toUserIdentity == constants.IDENTITY_NORMAL_USER {
		_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_INITIATED_CALL_COUNT, 1)
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_CALL, nil)

	c.Result(constants.SUCCESS, nil)
	return
}

/*
* 男性快速打电话(请求对聊)
1、随机中在线、空闲状态主播中选取一个主播
2、向所选取的主播打电话（Call函数）
3、返回给男性用户选取的主播信息，已拨打等待中
*/
func (c *VideocallController) QuickCall() {

	req := dtos.QuickCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	anchorMatched, errNoMatch := services.QuickCallMatchLogic(req.UserId)
	if errNoMatch != constants.SUCCESS {
		_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		c.Error("QuickCallMatchLogic(%v): %v", req.UserId, err)
		c.Result(errNoMatch, nil)
		return
	}

	if anchorMatched == nil {
		//// 未匹配到主播, 加入需求池
		//err = services.SubmitUserToAnchorMatchPool(req.UserId)
		//if err != nil {
		//	_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		//	c.Error("AddUserToUserPool(%v): %v", req.UserIdStr, err)
		//	c.Result(constants.ERR_SYS, nil)
		//	return
		//}

		c.Result(constants.ERR_REMATCH_ANCHOR_TASK, nil)
		return
	}

	anchorId := anchorMatched.AnchorId
	anchorIdStr := fmt.Sprintf("%d", anchorId)

	errNoNotice := services.QuickCallNotice(req.UserId, anchorMatched.AnchorId)
	if errNoNotice != constants.SUCCESS {
		_ = Redisserver.ReleaseCallUserLock(anchorIdStr)
		_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		if errNoNotice == constants.ERR_USER_LOW_BALANCE {
			c.Warn("用户userId:%d 余额不足，无法快速呼叫", req.UserId)
		} else {
			c.Error("QuickCallNotice(%v, %v): %v", req.UserId, anchorMatched.AnchorId, errNoNotice)
		}

		c.Result(errNoNotice, nil)
		return
	}

	_ = services.AfterMatchAnchor(req.UserId, anchorId)

	res := dtos.QuickCallRes{
		AnchorId:    anchorIdStr,
		Status:      anchorMatched.Status,
		SubRegionId: anchorMatched.SubRegionId,
	}

	// 统计相关
	_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_CALLED_REQUEST_COUNT, 1)

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_QUICK_CALL_SUCCESS, nil)

	c.Result(constants.SUCCESS, res)
	return
}

func (c *VideocallController) QuickCallMatch() {

	req := dtos.QuickCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	anchorMatched, errNoMatch := services.QuickCallMatchLogic(req.UserId)
	if errNoMatch != constants.SUCCESS {
		_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		c.Error("QuickCallMatchLogic(%v): %v", req.UserId, err)
		c.Result(errNoMatch, nil)
		return
	}

	if anchorMatched == nil {
		// 未匹配到主播, 加入需求池
		err = services.SubmitUserToAnchorMatchPool(req.UserId)
		if err != nil {
			_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
			c.Error("AddUserToUserPool(%v): %v", req.UserIdStr, err)
			c.Result(constants.ERR_SYS, nil)
			return
		}

		c.Result(constants.ERR_REMATCH_ANCHOR_TASK, nil)
		return
	}

	anchorId := anchorMatched.AnchorId
	anchorIdStr := fmt.Sprintf("%d", anchorId)

	errNoNotice := services.QuickCallNotice(req.UserId, anchorMatched.AnchorId)
	if errNoNotice != constants.SUCCESS {
		_ = Redisserver.ReleaseCallUserLock(anchorIdStr)
		_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
		if errNoNotice == constants.ERR_USER_LOW_BALANCE {
			c.Warn("用户userId:%d 余额不足，无法快速呼叫", req.UserId)
		} else {
			c.Error("QuickCallNotice(%v, %v): %v", req.UserId, anchorMatched.AnchorId, errNoNotice)
		}

		c.Result(errNoNotice, nil)
		return
	}

	_ = services.AfterMatchAnchor(req.UserId, anchorId)

	res := dtos.QuickCallRes{
		AnchorId:    anchorIdStr,
		Status:      anchorMatched.Status,
		SubRegionId: anchorMatched.SubRegionId,
	}

	// 统计相关
	_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_CALLED_REQUEST_COUNT, 1)

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_QUICK_CALL_SUCCESS, nil)

	c.Result(constants.SUCCESS, res)
	return
}

func (c *VideocallController) CancelQuickCall() {

	req := dtos.CancelQuickCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	// 把用户从需求池中移除
	err = services.RemoveUserFromUserPool(req.UserIdStr)
	if err != nil {
		c.Error("RemoveUserFromUserPool(%v): %v", req.UserIdStr, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}

	//// 统计相关
	//_ = models.AnchorWorkStatDec(anchorId, models.ANCHOR_STAT_TYPE_CALLED_REQUEST_COUNT, 1)

	c.Result(constants.SUCCESS, nil)
	return
}

/*
* 接听
1、判断anchor_info表中status状态是否为可接听状态
2、修改anchor_info表中status字段值为4
3、生成本次对聊唯一标识（可以取guid，便于之后查询使用）
4、向video_call_log表中添加对聊记录
5、向mq发布主播接听消息

	（1）消息订阅服务处理该条消息，通知男性用户主播已接听，并将本次对聊唯一标识通知给男性用户

6、返回应答中将本次对聊唯一标识等信息返回给主播
*/
func (c *VideocallController) AnswerCall() {
	req := dtos.AnswerCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	callUserId, _ := strconv.ParseInt(req.CallUserId, 10, 64)

	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	var anchorId, viewerId int64

	if identity == constants.IDENTITY_NORMAL_USER {
		anchorId = callUserId
		viewerId = userId
	} else {
		anchorId = userId
		viewerId = callUserId
	}

	//// 判断黑名单 (没必要了)
	//blackStatus := models.BlackStatus(userId, callUserId)
	//if blackStatus == constants.BLACK_STATUS_FROM_OTHER {
	//	c.Result(constants.ERR_BLACK_STATUS_FROM_OTHER, nil)
	//	return
	//} else if blackStatus == constants.BLACK_STATUS_FROM_ME {
	//	c.Result(constants.ERR_BLACK_STATUS_FROM_ME, nil)
	//	return
	//}

	var strGuid string
	strAnchorId := fmt.Sprintf("%d", anchorId)
	strViewerId := fmt.Sprintf("%d", viewerId)

	// 如果是男性判断至少有1分钟使用的金币余额
	if identity == constants.IDENTITY_NORMAL_USER {
		//isChecked, _, _, err := services.CheckMaleCallCost(viewerId, anchorId)
		checkRes, err := services.CheckMaleCallCost(viewerId, anchorId)
		if err != nil {
			c.Error("CheckMaleCallCost(%v, %v): %v", viewerId, anchorId, err)
			c.Result(constants.ERR_SYS, nil)
			return
		}
		if !checkRes.Checked {
			c.Result(constants.ERR_USER_LOW_BALANCE, nil)
			return
		}
	}

	// 1、判断anchor_info表中status状态是否为可接听状态
	anchorInfo, err := models.GetAnchor_infoByAnchorId(anchorId)
	if err != nil {
		c.Error("GetAnchor_infoByAnchorId(%+v): %v", anchorId, err)
		c.Result(constants.ERR_GET_ANCHOR, nil)
		return
	}

	if anchorInfo.Status != constants.ANCHOR_ONLINEIDLE && anchorInfo.Status != constants.ANCHOR_ONLINETAKINGORDERS {
		c.Result(constants.ERR_ANCHOR_NOT_ONLINETAKINGORDERS, nil)
		return
	}

	// 查找用户和主播的的regin_id（计费要用）
	anchorSubRegionId := fmt.Sprintf("%d", anchorInfo.SubRegionId)
	viewInfo, err := models.GetUsersByUserId(viewerId)
	if err != nil {
		c.Error("GetUsersByUserId(%+v): %v", viewerId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
		return
	}
	viewSubRegionId := fmt.Sprintf("%d", viewInfo.SubRegionId)

	//=========================== 事务操作 ==========================
	o := orm.NewOrm()
	tx, err := o.Begin()
	if err != nil {
		c.Result(constants.ERR_DB_BEGIN_TRANSACTION, nil)
		return
	}

	// 使用一个标志位来跟踪事务中的操作是否全部成功
	var errNo = constants.SUCCESS
	var guid int64

	// 如果所有操作都成功，errNo 仍为 SUCCESS，将在 defer 中提交事务
	defer func() {

		// 统计相关
		if identity == constants.IDENTITY_NORMAL_USER {
			_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_INITIATED_CONNECTED_COUNT, 1)
		} else {
			_ = models.AnchorWorkStatInc(anchorId, models.ANCHOR_STAT_TYPE_CALLED_CONNECTED_COUNT, 1)
		}

		// mq 同步用户信息
		go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_ANSWER_CALL, map[string]string{
			"to_user_id": req.CallUserId,
		})

		if errNo == constants.SUCCESS {
			if err = tx.Commit(); err != nil {
				c.Error("Commit transaction failed: %v", err)
				c.Result(constants.ERR_DB_COMMIT_TRANSACTION, nil)
			} else {
				logs.Debug("Transaction processed successfully")

				// 通知开启计费
				respData, err := services.ChargingStart(strGuid, strAnchorId, strViewerId, anchorSubRegionId, viewSubRegionId)
				if err != nil || respData.Code != 0 {
					c.Error("通知开启计费报错：services.ChargingStart(%s, %s, %s) return: (respData:%+v,err:%v)", strGuid, strAnchorId, strViewerId, respData, err)
					c.Result(constants.ERR_CHARGING_START_RETURN_FAIL, nil)
					return
				}

				// 6、返回应答中将本次对聊唯一标识等信息返回给拨打方
				res := map[string]interface{}{
					"guid": fmt.Sprintf("%d", guid),
				}
				_ = Redisserver.ReleaseCallUserLock(req.CallUserId)
				_ = Redisserver.ReleaseCallUserLock(req.UserIdStr)
				c.Result(constants.SUCCESS, res)
			}
		} else {
			if err = tx.Rollback(); err != nil {
				c.Error("Rollback transaction failed: %v", err)
				c.Result(constants.ERR_DB_ROLLBACK_TRANSACTION, nil)
			}
			c.Result(errNo, nil)
		}
	}()

	// 2、修改anchor_info表中status字段值为4
	anchorNew := models.Anchor_info{
		AnchorId:   anchorId,
		Status:     constants.ANCHOR_BUSY,
		ActiveTime: time.Now().Unix(),
	}
	if anchorInfo.Status == constants.ANCHOR_ONLINETAKINGORDERS {
		anchorNew.FastOrder = constants.FAST_ORDER_ON
	} else {
		anchorNew.FastOrder = constants.FAST_ORDER_OFF
	}
	err = models.TxUpdateAnchor_infoByAnchorIdAndFields(tx, &anchorNew, []string{"status", "active_time", "fast_order"})
	if err != nil {
		c.Error("UpdateAnchor_infoByAnchorIdAndFields(%+v): %v", anchorNew, err)
		errNo = constants.ERR_UPDATE_ANCHOR_STATUS
		return
	}

	// 3. 修改男性用户表users的状态改为status字段值为4
	userNew := models.Users{
		UserId:     viewerId,
		Status:     constants.ANCHOR_BUSY,
		UpdateTime: time.Now().Unix(),
	}
	err = models.TxUpdateUsersByUserIdAndFields(tx, &userNew, []string{"status", "update_time"})
	if err != nil {
		c.Error("UpdateUsersByUserIdAndFields(%+v): %v", userNew, err)
		errNo = constants.ERR_UPDATE_USER_STATUS
		return
	}

	// 3、生成本次对聊唯一标识（可以取guid，便于之后查询使用）
	guid, err = utils.CreateOrder()
	if err != nil {
		c.Error("CreateOrder(): %v", err)
		errNo = constants.ERR_CREATE_ORDER
		return
	}
	c.Debug("guid:", guid)
	if guid > 0 {
		strGuid = fmt.Sprintf("%d", guid)
	}

	// 4、向video_call_log表中添加对聊记录
	videoCallLog := models.Video_call_log{
		Id:         guid,
		AnchorId:   anchorId,
		ViewerId:   viewerId,
		CallType:   req.CallType,
		CallerId:   callUserId,
		BeginTime:  time.Now().Unix(),
		UpdateTime: time.Now().Unix(),
	}
	_, err = models.TxAddVideo_call_log(tx, &videoCallLog)
	if err != nil {
		c.Error("AddVideo_call_log(%+v): %v", videoCallLog, err)
		errNo = constants.ERR_ADD_VIDEO_CALL_LOG
		return
	}

	// 查询主播价格信息
	priceInfo, err := services.GetAnchorPriceInfo(anchorId, constants.PRICE_TYPE_ID_VIDEO_CALL)
	if err != nil {
		c.Error("GetAnchorPriceInfo(%v): %v", anchorId, err)
		errNo = constants.ERR_GET_CALL_PRICE
		return
	}
	payPrice := priceInfo.PayPrice
	anchorPrice := priceInfo.AnchorPrice

	// 查询用户信息详情【读缓存】
	anchorCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strAnchorId, constants.IDENTITY_ANCHOR)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}
	viewerCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, strViewerId, constants.IDENTITY_NORMAL_USER)
	if err != nil {
		c.Error("GetUserInfoFromRedis(%v): %v", strAnchorId, err)
		c.Result(constants.ERR_USER_NOT_EXIST, nil)
	}

	// 5、IM 发布接听消息
	// 5.1 通知对方
	streamId := fmt.Sprintf("stream-%d-%d", userId, time.Now().Unix())
	homeNum := fmt.Sprintf("video-%d-%d", userId, callUserId)
	noticeUserId := req.CallUserId

	reqIm := services.AssembleImVideocallAnswerCallRequest(identity, noticeUserId, strGuid, streamId, homeNum, payPrice, anchorPrice, anchorId, viewerCacheInfo, anchorCacheInfo)
	err = services.ImVideocallAnswer(&reqIm)
	if err != nil {
		c.Error("services.ImVideocallAnswer(%+v): %v", reqIm, err)
		errNo = constants.ERR_IM_SENDM_FAIL
		return
	}

	// 5.2 通知自己
	noticeUserId = req.UserIdStr
	streamId = fmt.Sprintf("stream-%d-%d", callUserId, time.Now().Unix())
	reqIm2 := services.AssembleImVideocallAnswerCallRequest(identity, noticeUserId, strGuid, streamId, homeNum, payPrice, anchorPrice, anchorId, viewerCacheInfo, anchorCacheInfo)
	err = services.ImVideocallAnswer(&reqIm2)
	if err != nil {
		c.Error("IM发布接听信息通知自己失败:ImConfirmCall(%+v): %v", reqIm2, err)
		errNo = constants.ERR_IM_SENDM_FAIL
		return
	}

	return
}

// 拨打方确认拨打通知（废弃）
//func (c *VideocallController) ConfirmCall() {
//	/**
//	1、判断是否为本次对话的发起方
//	2、修改video_call_log表中status字段值为2
//	3、向mq发布确认接听消息，消息订阅服务处理该条消息
//		（1）向女性用户发送确认接听消息
//		（2）向男性用户发送确认接听消息
//	4、返回应答中将本次对聊唯一标识等信息返回给主播
//	*/
//	// 1、判断是否为本次对话的发起方
//	params := dtos.ConfirmCallParams{}
//	json.Unmarshal(c.Ctx.Input.RequestBody, &params)
//	userId, _ := strconv.ParseInt(params.UserId, 10, 64)
//	toUserId, _ := strconv.ParseInt(params.ToUserId, 10, 64)
//
//	// 进行数据验证
//	err := dtos.ValidateParams(&params)
//	if err != nil {
//		c.Error("参数错误: ", err)
//		c.Result(constants.ERR_PARAM, nil)
//		return
//	}
//
//	anchorId := userId
//	if params.Gender == constants.SEX_MAILE {
//		anchorId = toUserId
//	}
//
//	streamId := fmt.Sprintf("stream-%d-%d", userId, time.Now().Unix())
//	err = services.ImConfirmCall(params.UserId, params.UserId, streamId, anchorId)
//	if err != nil {
//		c.Error("IM发布确认接听信息失败:confirmCall(%v, %v, %s): %v", params.UserId, params.UserId, streamId, err)
//		c.Result(constants.ERR_IM_SENDM_FAIL, nil)
//		return
//	}
//
//	c.Result(constants.SUCCESS, nil)
//	return
//}

// 挂断
func (c *VideocallController) StopCall() {
	/**
	1、判断挂断操作是男性用户发起的还是主播发起的挂断
	2、根据对聊唯一标识修改video_call_log表中记录，设置结束时间
	3、修改anchor_info表中status字段值为2
	4、向mq发布挂断信息，消息订阅服务处理该条消息
		（1）男性用户挂断则向主播发送用户已挂断消息
		（2）主播挂断则向用户发送主播已挂断消息
	*/
	// 1、判断挂断操作是男性用户发起的还是主播发起的挂断
	req := dtos.StopCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	userId := req.UserId
	toUserId, _ := strconv.ParseInt(req.ToUserId, 10, 64)
	callerId, _ := strconv.ParseInt(req.CallerId, 10, 64)
	guid, _ := strconv.ParseInt(req.Guid, 10, 64)

	// 获取用户身份
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	var anchorId, viewerId int64
	if identity == constants.IDENTITY_NORMAL_USER {
		anchorId = toUserId
		viewerId = userId
	} else {
		anchorId = userId
		viewerId = toUserId
	}

	if guid > 0 {
		// 1. 根据 guid 查询 video_call_log 表中记录
		videoCallLog, err := models.GetVideo_call_logById(guid)
		if err != nil {
			c.Error("查询 video_call_log 表中记录失败:GetVideo_call_logById(%s): %v", guid, err)
			c.Result(constants.ERR_GET_VIDEO_CALL_LOG, nil)
			return
		}

		if videoCallLog.EndTime > 0 {
			c.Error("通话已经结束，不能重复挂断")
			c.Result(constants.ERR_CALL_ALREADY_END, nil)
			return
		}
		callerId = videoCallLog.CallerId
	}

	// 挂断逻辑
	err = services.StopCallLogic(guid, userId, anchorId, viewerId, callerId, req.OperationType)
	if err != nil {
		c.Error("StopCallLogic(%s, %v, %v, %v): %v", req.Guid, userId, anchorId, viewerId, err)
		c.Result(constants.ERR_STOP_CALL_LOGIC, nil)
		return
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_STOP_CALL, map[string]string{
		"to_user_id": req.ToUserId,
	})

	c.Result(constants.SUCCESS, nil)
	return

}

// 系统主动中断通话
func (c *VideocallController) InterruptsCall() {
	req := dtos.InterruptsCallParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	anchorId, _ := strconv.ParseInt(req.AnchorId, 10, 64)
	guid, _ := strconv.ParseInt(req.Guid, 10, 64)

	err = services.InterruptsCallLogic(guid, req.UserId, anchorId, req.OperationType)
	if err != nil {
		c.Error("InterruptsCallLogic(%s, %v, %v, %v): %v", guid, req.UserId, anchorId, req.OperationType, err)
		c.Result(constants.ERR_INTERRUPT_CALL_LOGIC, nil)
		return
	}

	// mq 同步用户信息
	go services.SyncUserData(req.UserIdStr, constants.DATA_UPDATED_STOP_CALL, map[string]string{
		"to_user_id": req.AnchorId,
	})

	c.Result(constants.SUCCESS, nil)
	return
}

// 收益结算数据获取（对聊结束后）
func (c *VideocallController) ObtainIncome() {
	/**
	1、根据对聊唯一标识查询video_call_log表中数据获取时长收益和礼物收益
	*/

	req := dtos.ObtainIncomeParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	guid, _ := strconv.ParseInt(req.Guid, 10, 64)

	// 1. 根据 guid 查询 video_call_log 表中记录
	videoCallLog, err := models.GetVideo_call_logById(guid)
	if err != nil {
		c.Error("查询 video_call_log 表中记录失败:GetVideo_call_logById(%v): %v", guid, err)
		c.Result(constants.ERR_GET_VIDEO_CALL_LOG, nil)
		return
	}

	res := dtos.ObtainIncomeRes{
		Guid:           fmt.Sprintf("%d", videoCallLog.Id),
		AnchorId:       fmt.Sprintf("%d", videoCallLog.AnchorId),
		ViewerId:       fmt.Sprintf("%d", videoCallLog.ViewerId),
		BeginTime:      fmt.Sprintf("%d", videoCallLog.BeginTime),
		EndTime:        fmt.Sprintf("%d", videoCallLog.EndTime),
		CallType:       videoCallLog.CallType,
		DurationIncome: videoCallLog.DurationIncome,
		GiftIncome:     fmt.Sprintf("%d", videoCallLog.GiftIncome),
	}

	c.Result(constants.SUCCESS, res)
	return
}

func (c *VideocallController) KeepAlive() {
	req := dtos.KeepAliveParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}
	userId := req.UserId
	identity, err := models.GetUserIdentityByUserId(userId)
	if err != nil {
		c.Error("获取用户身份失败:GetUserIdentityByUserId(%v): %v", userId, err)
		c.Result(constants.ERR_GET_USER_IDENTITY_FAIL, nil)
		return
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		user := models.Users{
			UserId:     req.UserId,
			UpdateTime: time.Now().Unix(),
		}
		err = models.UpdateUsersByUserIdAndFields(&user, []string{"update_time"})
	} else {
		anchor := models.Anchor_info{
			AnchorId:   req.UserId,
			ActiveTime: time.Now().Unix(),
		}
		err = models.UpdateAnchor_infoByAnchorIdAndFields(&anchor, []string{"active_time"})

		if identity == constants.IDENTITY_ANCHOR {
			// 增加主播在线时长
			_ = models.AnchorWorkStatInc(req.UserId, models.ANCHOR_STAT_TYPE_ONLINE_DURATION_MINUTES, 1)
		}
	}

	if err != nil {
		c.Error("更新用户信息失败: ", err)
		c.Result(constants.ERR_KEEPALIVE_UPDATE_USER_INFO, nil)
		return
	}

	c.Result(constants.SUCCESS, nil)
	return
}

// 拉黑、取消拉黑
func (c *VideocallController) UpdateBlacklist() {

	req := dtos.VideocallUpdateBlackParams{}
	err := dtos.Bind(&req, c.Ctx)
	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	ids := []int64{req.UserId, req.DistUserId}

	userinfo, err := models.GetUser_profilesById(ids)

	if err != nil {
		c.Error("查询用户信息:GetUser_profilesById(%v): %v", ids, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}

	blackCount, err := models.GetBlack_listCountByOperatorId(req.UserId)
	if err != nil {
		c.Error("查询黑名单数量:GetBlack_listCountByOperatorId(%v): %v", req.UserId, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}

	if blackCount >= configs.BlackListMaxCount {
		c.Error("黑名单数量超过限制")
		c.Result(constants.ERR_BLACKLIST_COUNT_LIMIT, nil)
		return
	}

	userProfiles := map[int64]models.User_profile{}

	for _, v := range userinfo {
		userProfiles[v.UserId] = v
	}

	if _, ok := userProfiles[req.UserId]; !ok {
		cocoUserId := fmt.Sprintf("%d", req.UserId)
		cocoAnchor, err := services.GetCocoUserbalance(cocoUserId)
		if err != nil || cocoAnchor.Result != 0 {
			c.Error("查询用户信息:GetCocoUserbalance(%v): %v", cocoUserId, err)
			c.Result(constants.ERR_ANCHOR_NOT_FOUND, nil)
			return
		}
	}

	if _, ok := userProfiles[req.DistUserId]; !ok {
		cocoUserId := fmt.Sprintf("%d", req.DistUserId)
		cocoAnchor, err := services.GetCocoUserbalance(cocoUserId)
		if err != nil || cocoAnchor.Result != 0 {
			c.Error("查询用户信息:GetCocoUserbalance(%v): %v", cocoUserId, err)
			c.Result(constants.ERR_ANCHOR_NOT_FOUND, nil)
			return
		}
	}

	// 查询黑名单信息
	err = models.SetBlack_Status(req.UserId, req.DistUserId, req.Status)

	if err != nil {
		c.Warn("设置黑名单失败: ", err)
		c.Result(constants.ERR_SET_BLACK_STATUS, nil)
		return
	}

	c.Result(constants.SUCCESS, nil)
}

// 获取拉黑主播列表
func (c *VideocallController) GetBlacklist() {

	req := dtos.VideocallGetBlacklistParams{}
	err := dtos.Bind(&req, c.Ctx)
	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	perPage := 50

	// 查询黑名单列表
	blackList, err := models.GetAllBlack_listByOperatorId(req.UserId, req.Page, perPage)
	count, err := models.GetBlack_listCountByOperatorId(req.UserId)

	if err != nil {
		c.Result(constants.ERR_GET_BLACK_LIST, nil)
		return
	}

	list := []dtos.BlacklistItem{}

	if len(blackList) > 0 {
		ids := []string{}
		for _, v := range blackList {
			ids = append(ids, fmt.Sprintf("%d", v.TargetId))
		}
		userInfoList, err := services.CocoGetUserinfo(ids)

		if err != nil {
			c.Result(constants.ERR_SYS, nil)
			return
		}

		userInfoMap := map[string]services.CocoUserInfoItem{}
		for _, v := range *userInfoList {
			userInfoMap[v.UserId] = v
		}

		for _, v := range blackList {
			list = append(list, dtos.BlacklistItem{
				UserId:     v.TargetId,
				Nick:       userInfoMap[fmt.Sprintf("%d", v.TargetId)].NickName,
				HeadImg:    userInfoMap[fmt.Sprintf("%d", v.TargetId)].AvatarUrl,
				CreateTime: int(v.CreateTime),
			})
		}
	}

	res := dtos.VideocallGetBlacklistResult{
		Page: dtos.PageData{
			CurrentPage: req.Page,
			PerPage:     perPage,
			TotalPages:  int(math.Ceil(float64(count) / float64(perPage))),
			TotalItems:  int(count),
		},
		List: list,
	}

	c.Result(constants.SUCCESS, res)
}

func (c *VideocallController) GetIsBlackDist() {
	req := dtos.VideocallGetIsBlackDistPrams{}
	err := dtos.Bind(&req, c.Ctx)
	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	status := constants.BLACKLIST_STATUS_UNBLOCKED
	isBlack := models.IsBlackDist(req.UserId, req.DistUserId)
	if isBlack {
		status = constants.BLACKLIST_STATUS_BLOCKED
	}

	res := dtos.VideocallGetIsBlackDistResult{
		Status: status,
	}

	c.Result(constants.SUCCESS, res)
}

// 评价
func (c *VideocallController) Evaluate() {

	req := dtos.VideocallEvaluateParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	guid, _ := strconv.ParseInt(req.Guid, 10, 64)

	// 查询对聊记录
	videoCallLog, err := models.GetVideo_call_logByUser(guid, req.UserId)

	if err != nil {
		c.Result(constants.ERR_EVALUATE_NOT_FOUND, nil)
		return
	}

	if videoCallLog.EndTime == 0 {
		c.Result(constants.ERR_ORDER_NOEND_TO_EVALUATE, nil)
		return
	}

	evaluateData, err := models.GetVideo_call_evaluateByGuid(guid)

	if err == nil && evaluateData.Id > 0 {
		c.Result(constants.ERR_ORDER_EVALUATED_ALREADY, nil)
		return
	}

	// 评价
	evaluate := models.Video_call_evaluate{
		Guid:         videoCallLog.Id,
		AnchorId:     videoCallLog.AnchorId,
		ViewerId:     videoCallLog.ViewerId,
		EvaluateType: req.Type,
		CreateTime:   time.Now().Unix(),
	}

	_, err = models.AddVideo_call_evaluate(&evaluate)

	if err != nil {
		c.Result(constants.ERR_ORDER_EVALUATED_FAILD, nil)
		return
	}

	c.Result(constants.SUCCESS, nil)
}

// 获取用户信息
func (c *VideocallController) UserInfo() {
	req := dtos.UserInfoParams{}
	err := dtos.Bind(&req, c.Ctx)
	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	filterUserId, _ := strconv.ParseInt(req.FilterUserId, 10, 64)
	userInfo, err := models.GetUser_profileByUserId(filterUserId)

	if err != nil {
		c.Error("查询用户信息:GetUser_profilesById(%v): %v", req.UserId, err)
		c.Result(constants.ERR_SYS, nil)
		return
	}

	//userCacheInfo, err := services.GetUserInfoFromRedis(Redisserver, req.UserIdStr, userInfo.Identity)
	//
	//if userCacheInfo == nil {
	//	c.Result(constants.ERR_USER_NOT_FOUND, nil)
	//	return
	//}

	res := dtos.UserInfoRes{
		UserId:   userInfo.UserId,
		Identity: userInfo.Identity,
		Gender:   userInfo.Gender,
	}

	if userInfo.Identity == 1 {
		viewerInfo, err := models.GetUsersByUserId(filterUserId)
		if err != nil {
			c.Error("查询用户信息:GetUsersByUserId(%v): %v", filterUserId, err)
			c.Result(constants.ERR_SYS, nil)
		}
		res.Status = viewerInfo.Status
		res.Nick = viewerInfo.Nick
		res.HeadImg = viewerInfo.HeadImg
		res.SubRegionId = viewerInfo.SubRegionId
		res.Age = viewerInfo.Age
	} else {
		anchorInfo, err := models.GetAnchor_infoByAnchorId(filterUserId)
		if err != nil {
			c.Error("查询用户信息:GetAnchor_infoByAnchorId(%v): %v", filterUserId, err)
			c.Result(constants.ERR_SYS, nil)
		}
		res.Status = anchorInfo.Status
		res.Nick = anchorInfo.Nick
		res.HeadImg = anchorInfo.HeadImg
		res.SubRegionId = anchorInfo.SubRegionId
		res.Age = anchorInfo.Age
	}

	// 查询对应的国旗信息并更新
	subRegionInfo, err := models.GetSub_region_configurationByRegionId(res.SubRegionId)
	if err != nil {
		c.Error("查询用户信息:GetSub_region_configurationByRegionId(%v): %v", res.SubRegionId, err)
		c.Result(constants.ERR_SYS, nil)
	}
	res.FlagImage = services.CompleteURL(subRegionInfo.FlagImage)

	// 在线状态

	c.Result(constants.SUCCESS, res)
}

// 测试获取authCode
func (c *VideocallController) GetAuthCode() {

	reqBody := map[string]interface{}{
		"user_id":       "190302",
		"gender":        2,
		"from_type":     1,
		"to_user_id":    "190308",
		"msg_type":      0,
		"msg":           "hello",
		"duration":      0,
		"chat_scenario": 1,
		"data": map[string]interface{}{
			"test": 111,
		},
	}

	bodyBytes, err := json.Marshal(reqBody)

	if err != nil {
		c.Result(constants.ERR_PARAM, nil)
	}
	logs.Info("请求体数据bodyBytes: %s", string(bodyBytes))
	//req, err = http.NewRequest(method, url, bytes.NewBuffer(bodyBytes))

	codeKey := configs.FilterCodeKey
	serverName := configs.FilterCodeServerName

	authCode := filters.GenerateAuthCode(string(bodyBytes), codeKey)
	serverCode := filters.GenerateServerCode(serverName, codeKey)

	res := map[string]interface{}{
		"authCode":   authCode,
		"serverCode": serverCode,
	}

	c.Result(constants.SUCCESS, res)
}

// 测试获取sonyflakeId
func (c *VideocallController) GetSonyflakeId() {

	id, err := utils.CreateOrder()
	if err != nil {
		c.Result(constants.ERR_SYS, nil)
		return
	}

	// 打印化境变量
	c.Info("环境变量: RunMode=%s, EnvName=%s, ProductName=%s", configs.RunMode, configs.EnvName, configs.ProductName)

	//// 写一个方法，循环10万次，每次打印生成的id
	//for i := 0; i < 100000; i++ {
	//	c.Info("i = %d", i)
	//}

	res := map[string]interface{}{
		"sony_flake_id": id,
		"RunMode":       configs.RunMode,
		"EnvName":       configs.EnvName,
		"ProductName":   configs.ProductName,
	}

	c.Result(constants.SUCCESS, res)
}

// 测试事务
func (c *VideocallController) TestTransaction() {
	o := orm.NewOrm()
	tx, err := o.Begin()
	if err != nil {
		c.Result(constants.ERR_DB_BEGIN_TRANSACTION, nil)
		return
	}

	// 使用一个标志位来跟踪事务中的操作是否全部成功
	var errNo = constants.SUCCESS
	var guid int64

	// 如果所有操作都成功，allOperationsSuccessful 仍为 true，将在 defer 中提交事务
	defer func() {
		if errNo == constants.SUCCESS {
			if err = tx.Commit(); err != nil {
				c.Error("Commit transaction failed: %v", err)
				c.Result(constants.ERR_DB_COMMIT_TRANSACTION, nil)
			} else {
				logs.Debug("Transaction processed successfully")
				res := map[string]interface{}{
					"guid": guid, // 直接使用 guid，无需格式化
				}
				c.Result(constants.SUCCESS, res)
			}
		} else {
			if err = tx.Rollback(); err != nil {
				c.Error("Rollback transaction failed: %v", err)
				c.Result(constants.ERR_DB_ROLLBACK_TRANSACTION, nil)
			}
			c.Result(errNo, nil)
		}
	}()

	userInfo := models.Users{
		UserId: 10100,
		Status: constants.ANCHOR_BUSY,
	}
	if err = models.TxUpdateUsersByUserIdAndFields(tx, &userInfo, []string{"status"}); err != nil {
		c.Error("UpdateUsersByUserIdAndFields failed: %v", err)
		errNo = constants.ERR_UPDATE_USER_STATUS
		return
	}

	guid, err = utils.CreateOrder()
	if err != nil {
		c.Error("CreateOrder failed: %v", err)
		errNo = constants.ERR_CREATE_ORDER
		return
	}
	c.Debug("guid: %s", guid)

	videoCallLog := models.Video_call_log{
		Id:         guid,
		AnchorId:   190302,
		ViewerId:   10100,
		CallType:   0,
		BeginTime:  time.Now().Unix(),
		UpdateTime: time.Now().Unix(),
	}
	if _, err = models.TxAddVideo_call_log(tx, &videoCallLog); err != nil {
		c.Error("AddVideo_call_log failed: %v", err)
		errNo = constants.ERR_ADD_VIDEO_CALL_LOG
		return
	}
}

// 测试协程池
func (c *VideocallController) TestSubmitTaskToPool() {
	//	req := dtos.SubmitTaskToPoolParams{}
	//	err := dtos.Bind(&req, c.Ctx)
	//
	//	if err != nil {
	//		c.Error("参数错误: ", err)
	//		c.Result(constants.ERR_PARAM, nil)
	//		return
	//	}
	//
	//	if req.SubmitType == 2 {
	//		cocoPushMessageReq := services.CocoPushMessageReq{
	//			NoticeUserId: req.UserId,
	//			UserId:       req.UserId,
	//			Data:         req.Data,
	//		}
	//		err = services.SubmitCocoPushTask(&CocoPushMessageReq)
	//
	//		if err != nil {
	//			c.Error("协程池 2 任务数到达阈值，本次任务提交失败:", err)
	//			c.Result(constants.ERR_POOL_TASK_FULL, nil)
	//			return
	//		}
	//	} else {
	//		taskArgs := services.BalanceChangeNoticeTaskArgs{
	//			UserId: req.UserId,
	//		}
	//		err = services.SubmitBalanceChangeNoticeTask(&taskArgs)
	//
	//		if err != nil {
	//			c.Error("协程池 1 任务数到达阈值，本次任务提交失败:", err)
	//			c.Result(constants.ERR_POOL_TASK_FULL, nil)
	//			return
	//		}
	//	}
	//
	c.Result(constants.SUCCESS, nil)
}

func (c *VideocallController) TestCountPool() {

	res := tasks.AntsPoolStatus()

	c.Result(constants.SUCCESS, res)
}

// 模拟新增用户到用户匹配主播任务池
func (c *VideocallController) TestAddUserToAnchorMatchPool() {
	req := dtos.TestAddUserToAnchorMatchPoolParams{}
	err := dtos.Bind(&req, c.Ctx)

	if err != nil {
		c.Error("参数错误: ", err)
		c.Result(constants.ERR_PARAM, nil)
		return
	}

	err = services.SubmitUserToAnchorMatchPool(req.UserId)
	if err != nil {
		c.Error("用户匹配主播任务池，本次任务提交失败:", err)
		c.Result(constants.ERR_POOL_TASK_FULL, nil)
		return
	}

	c.Result(constants.SUCCESS, nil)
}
